/**
 * @class DateTime
 * @description 日期处理模块
 */
module.exports = class DateTime {
    constructor() {
        //默认日期展示格式
        this.defaultDateFormat = 'Y-m-d H:i:s';
        //默认时区
        this.defaultTimezone = 8;
        this.setTimeZone(this.defaultTimezone);
    }

    /**
     * 设置时区
     * @param {Number} timezone 时区
     */
    setTimeZone(timezone) {
        if (timezone) {
            this.timezone = parseInt(timezone);
        }
        return this;
    }

    /**
     * 获取 Date对象
     * @param {Date|Time} time
     */
    getDateObj(time) {
        return time ? new Date(time) : new Date();
    }

    /**
     * 获取毫秒/秒级时间戳
     * @param {DateTime} datetime 日期 例：'2022-04-21 00:00:00'
     * @param {Boolean} showSenconds 是否显示为秒级时间戳
     */
    getTime(datetime, showSenconds) {
        let time = this.getDateObj(datetime).getTime();
        if (showSenconds) {
            time = Math.trunc(time / 1000);
        }
        return time;
    }

    /**
     * 获取日期
     * @param {String} dateFormat 日期格式
     * @param {Time} time 时间戳
     */
    getDate(dateFormat, time) {
        return this.dateFormat(dateFormat, time);
    }

    /**
     * 获取日期在不同时区下的时间戳
     * @param {Date|Time}} time 日期或时间戳
     * @param {Object} timezone 时区
     */
    getTimeByTimeZone(time, timezone) {
        this.setTimeZone(timezone);
        const thisDate = time ? new Date(time) : new Date();
        const localTime = thisDate.getTime();
        const offset = thisDate.getTimezoneOffset();
        const utc = offset * 60000 + localTime;
        return utc + 3600000 * this.timezone;
    }

    /**
     * 获取时间信息
     * @param {Time} time 时间戳
     * @param {Boolean} full 是否完整展示, 为true时小于10的位会自动补0
     */
    getTimeInfo(time, full = true) {
        time = this.getTimeByTimeZone(time);
        const date = this.getDateObj(time);
        const retData = {
            nYear: date.getFullYear(),
            nMonth: date.getMonth() + 1,
            nWeek: date.getDay() || 7,
            nDay: date.getDate(),
            nHour: date.getHours(),
            nMinutes: date.getMinutes(),
            nSeconds: date.getSeconds(),
        };

        if (full) {
            for (const k in retData) {
                if (retData[k] < 10) {
                    retData[k] = '0' + retData[k];
                }
            }
        }
        return retData;
    }

    /**
     * 时间格式转换
     * @param {String} format 展示格式如:Y-m-d H:i:s
     * @param {Time} time 时间戳
     */
    dateFormat(format, time) {
        const timeInfo = this.getTimeInfo(time);
        format = format || this.defaultDateFormat;
        let date = format;
        if (format) {
            date = date.replace(/Y/, timeInfo.nYear);
        }
        if (format.indexOf('m') !== false) {
            date = date.replace(/m/, timeInfo.nMonth);
        }
        if (format.indexOf('d') !== false) {
            date = date.replace(/d/, timeInfo.nDay);
        }
        if (format.indexOf('H') !== false) {
            date = date.replace(/H/, timeInfo.nHour);
        }
        if (format.indexOf('i') !== false) {
            date = date.replace(/i/, timeInfo.nMinutes);
        }
        if (format.indexOf('s') !== false) {
            date = date.replace(/s/, timeInfo.nSeconds);
        }
        return date;
    }

    /**
     * 获取utc格式时间
     * @param {Date|Time} datetime 日期或时间戳
     */
    getUTC(datetime) {
        return this.getDateObj(datetime).toUTCString();
    }

    /**
     * 获取ISO 格式时间
     *  @param {Date|Time} datetime 日期或时间戳
     */
    getISO(datetime) {
        return this.getDateObj(datetime).toISOString();
    }

    /**
     * 获取两时间相差天数
     * @param {Time} time1 时间戳
     * @param {Time} time2 时间戳
     */
    getDiffDays(time1, time2) {
        if (!time1) {
            return false;
        }
        time2 = time2 ? time2 : this.getTime();

        let diffTime = time2 - time1;
        if (diffTime < 0) {
            diffTime = Math.abs(diffTime);
        }

        return Math.ceil(diffTime / 86400000);
    }

    /**
     * 字符串转时间戳
     * @param {Object} str 字符串类型的时间戳
     */
    strToTime(str) {
        if (Array.from(str).length === 10) {
            str += '000';
        }
        return this.getTime(parseInt(str));
    }

    /**
     * 根据设置的天数获取指定日期N天后（前）的时间戳
     * @param {Number} days 天数
     * @param {Date|Time} time 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定日期初始时间戳（当天00:00:00的时间戳）
     */
    getTimeBySetDays(days, time, getAll = false) {
        const date = this.getDateObj(time);
        date.setDate(date.getDate() + days);
        let startTime = date.getTime();
        if (!getAll) {
            const realdate = this.getDate('Y-m-d 00:00:00', startTime);
            startTime = this.getTimeByDateAndTimezone(realdate);
        }
        return startTime;
    }

    /**
     * 根据设置的小时数获取指定日期N小时后（前）的时间戳
     * @param {Number} hours 小时数
     * @param {Date|Time} time 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定时间初始时间戳（该小时00:00的时间戳）
     */
    getTimeBySetHours(hours, time, getAll = false) {
        const date = this.getDateObj(time);
        date.setHours(date.getHours() + hours);
        let startTime = date.getTime();
        if (!getAll) {
            const realdate = this.getDate('Y-m-d H:00:00', startTime);
            startTime = this.getTimeByDateAndTimezone(realdate);
        }
        return startTime;
    }

    /**
     * 根据设置的周数获取指定日期N周后（前）的时间戳
     * @param {Number} weeks 周数
     * @param {Date|Time} time 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定日期初始时间戳（当天00:00:00的时间戳）
     */
    getTimeBySetWeek(weeks, time, getAll = false) {
        const date = this.getDateObj(time);
        const dateInfo = this.getTimeInfo(time);
        const day = dateInfo.nWeek;
        const offsetDays = 1 - day;
        weeks = weeks * 7 + offsetDays;
        date.setDate(date.getDate() + weeks);
        let startTime = date.getTime();
        if (!getAll) {
            const realdate = this.getDate('Y-m-d 00:00:00', startTime);
            startTime = this.getTimeByDateAndTimezone(realdate);
        }
        return startTime;
    }

    /**
     * 根据设置的月数获取指定日期N月后（前）的时间戳
     * @param {Number} monthes 月数
     * @param {Date|Time} time 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定日期初始时间戳（当天00:00:00的时间戳）
     */
    getTimeBySetMonth(monthes, time, getAll = false) {
        const date = this.getDateObj(time);
        date.setMonth(date.getMonth() + monthes);
        let startTime = date.getTime();
        if (!getAll) {
            const realdate = this.getDate('Y-m-01 00:00:00', startTime);
            startTime = this.getTimeByDateAndTimezone(realdate);
        }
        return startTime;
    }

    /**
     * 根据设置的季度数获取指定日期N个季度后（前）的时间戳
     * @param {Number} quarter 季度
     * @param {Date|Time} time 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定日期初始时间戳（当天00:00:00的时间戳）
     */
    getTimeBySetQuarter(quarter, time, getAll = false) {
        const date = this.getDateObj(time);
        const dateInfo = this.getTimeInfo(time);
        date.setMonth(date.getMonth() + quarter * 3);
        const month = date.getMonth() + 1;
        let quarterN;
        let mm;
        if ([1, 2, 3].indexOf(month) > -1) {
            // 第1季度
            mm = '01';
        } else if ([4, 5, 6].indexOf(month) > -1) {
            // 第2季度
            mm = '04';
        } else if ([7, 8, 9].indexOf(month) > -1) {
            // 第3季度
            mm = '07';
        } else if ([10, 11, 12].indexOf(month) > -1) {
            // 第4季度
            mm = '10';
        }
        let yyyy = date.getFullYear();
        let startTime = date.getTime();
        if (!getAll) {
            const realdate = this.getDate(`${yyyy}-${mm}-01 00:00:00`, startTime);
            startTime = this.getTimeByDateAndTimezone(realdate);
        }
        return startTime;
    }

    /**
     * 根据设置的年数获取指定日期N年后（前）的时间戳
     * @param {Number} year 月数
     * @param {Date|Time} time 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定日期初始时间戳（当天00:00:00的时间戳）
     */
    getTimeBySetYear(year, time, getAll = false) {
        const date = this.getDateObj(time);
        date.setFullYear(date.getFullYear() + year);
        let startTime = date.getTime();
        if (!getAll) {
            const realdate = this.getDate('Y-01-01 00:00:00', startTime);
            startTime = this.getTimeByDateAndTimezone(realdate);
        }
        return startTime;
    }

    /**
     * 根据时区获取指定时间的偏移时间
     * @param {Date|Time} 指定的日期或时间戳
     * @param {Number} timezone 时区
     */
    getTimeByDateAndTimezone(date, timezone) {
        if (!timezone) {
            timezone = this.timezone;
        }
        const thisDate = this.getDateObj(date);
        const thisTime = thisDate.getTime();
        const offset = thisDate.getTimezoneOffset();
        const offsetTime = offset * 60000 + timezone * 3600000;
        return thisTime - offsetTime;
    }

    /**
     * 根据指定的时间类型获取时间范围
     * @param {String} type 时间类型 hour:小时 day:天 week:周 month：月
     * @param {Number} offset 时间的偏移量
     * @param {Date|Time} thistime 指定的日期或时间戳
     * @param {Boolean} getAll 是否获取完整时间戳，为 false 时返回指定日期初始时间戳（当天00:00:00的时间戳）
     */
    getTimeDimensionByType(type, offset = 0, thistime, getAll = false) {
        let startTime = 0;
        let endTime = 0;
        switch (type) {
            case 'hour': {
                startTime = this.getTimeBySetHours(offset, thistime, getAll);
                endTime = getAll ? startTime : startTime + 3599999;
                break;
            }
            case 'day': {
                startTime = this.getTimeBySetDays(offset, thistime, getAll);
                endTime = getAll ? startTime : startTime + 86399999;
                break;
            }
            case 'week': {
                startTime = this.getTimeBySetWeek(offset, thistime, getAll);
                endTime = getAll ? startTime + 86400000 * 6 : startTime + 86400000 * 6 + 86399999;
                break;
            }
            case 'month': {
                startTime = this.getTimeBySetMonth(offset, thistime, getAll);
                const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime));
                const nextMonthFirstDayTime = new Date(date.getFullYear(), date.getMonth() + 1, 1).getTime();
                endTime = getAll
                    ? nextMonthFirstDayTime - 86400000
                    : this.getTimeByDateAndTimezone(nextMonthFirstDayTime) - 1;
                break;
            }
            case 'quarter': {
                startTime = this.getTimeBySetQuarter(offset, thistime, getAll);
                const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime));
                const nextMonthFirstDayTime = new Date(date.getFullYear(), date.getMonth() + 3, 1).getTime();
                endTime = getAll
                    ? nextMonthFirstDayTime - 86400000
                    : this.getTimeByDateAndTimezone(nextMonthFirstDayTime) - 1;
                break;
            }
            case 'year': {
                startTime = this.getTimeBySetYear(offset, thistime, getAll);
                const date = this.getDateObj(this.getDate('Y-m-d H:i:s', startTime));
                const nextFirstDayTime = new Date(date.getFullYear() + 1, 0, 1).getTime();
                endTime = getAll ? nextFirstDayTime - 86400000 : this.getTimeByDateAndTimezone(nextFirstDayTime) - 1;
                break;
            }
        }
        return {
            startTime,
            endTime,
        };
    }
};
